#include "mesh.h"

mesh::mesh(const std::string &type, const unsigned int nx, const unsigned int ny, const double xmax,
           const double ymax)
{
    _type = type;
    _nx = nx;
    _ny = ny;
    _xmax = xmax;
    _ymax = ymax;
}

mesh::~mesh()
{
}

void mesh::generateMesh()
{
    // 根据nx和ny 以及xmax, ymax产生网格
    std::vector<double> p_temp(dim);
    std::vector<unsigned int> e_temp(_type == "Quadrilateral" ? 4 : 3);
    double x_temp;
    double y_temp;
    double dx = _xmax / _nx;
    double dy = _ymax / _ny;
    if (dim == 2)
    {
        // 2d
        for (int i = 0; i <= _nx; i++)
        {
            for (int j = 0; j <= _ny; j++)
            {
                p_temp[0] = i * dx;
                p_temp[1] = j * dy;
                mesh_points.push_back(p_temp);
            }
        }
    }
    else
    {
    }
    _elem_num = _nx * _ny;
    _points_num = static_cast<unsigned int>(mesh_points.size());

    int row, col;
    for (auto i = 0; i < _elem_num; i++)
    {
        row = i % _ny;
        col = i / _ny;
        // 单元在第row行第col列
        // 第一点
        e_temp[0] = col * (_ny + 1) + row;
        e_temp[1] = e_temp[0] + _ny + 1;
        e_temp[2] = e_temp[1] + 1;
        e_temp[3] = e_temp[2] - _ny - 1;
        elem_topo.push_back(e_temp);
    }
}

void mesh::getNeighborElem()
{
    std::vector<int> n_temp(10, -1);
    int counter = 0;
    for (int i = 0; i < _points_num; i++)
    {
        // 对i节点找到所有含有i的网格
        for (int j = 0; j < _elem_num; j++)
        {
            for (int k = 0; k < 4; k++)
            {
                if (elem_topo[j][k] == i)
                {
                    n_temp[counter] = j;
                    counter++;
                    break;
                }
            }
        }
        neighborElem.push_back(n_temp);
        n_temp = std::vector<int>(10, -1); // 重新初始化
        counter = 0;
    }
}

void mesh::getNeighborNodes()
{
    std::vector<int> n_temp(10, -1);
    std::vector<unsigned int> top;
    for (int i = 1; i < _nx; i++)
    {
        top.push_back((i + 1) * _ny + i);
    }

    for (int i = 0; i < _points_num; i++)
    {
        // 判定是否边界点
        // 左边界
        if (0 <= i && i <= _ny)
        {
            if (i == 0)
            {
                n_temp[0] = 1;
                n_temp[1] = _ny + 1;
            }
            if (i == _ny)
            {
                n_temp[0] = _ny - 1;
                n_temp[1] = 2 * _ny + 1;
            }

            if (i != 0 && i != _ny)
            {
                n_temp[0] = i - 1;
                n_temp[1] = i + 1;
                n_temp[2] = i + _ny + 1;
            }
        }

        // 右边界

        if (i <= _points_num - 1 && i >= _points_num - 1 - _ny)
        {
            //
            if (i == _points_num - 1)
            {
                n_temp[0] = i - 1;
                n_temp[1] = i - 1 - _ny;
            }

            if (i == _points_num - 1 - _ny)
            {
                n_temp[0] = i + 1;
                n_temp[1] = i - _ny - 1;
            }
            if (i != _points_num - 1 && i != _points_num - 1 - _ny)
            {
                n_temp[0] = i - 1;
                n_temp[1] = i + 1;
                n_temp[2] = i - _ny - 1;
            }
        }

        // 下边界
        if (i % (_ny + 1) == 0 && i != 0)
        {
            // 去除下边界角点
            if (i != 0 && i != _points_num - _ny - 1)
            {
                n_temp[0] = i - _ny - 1;
                n_temp[1] = i + _ny + 1;
                n_temp[2] = i + 1;
            }
        }

        // 上边界
        for (int l = 0; l < top.size(); l++)
        {
            if (i == top.at(l))
            {
                n_temp[0] = i - _ny - 1;
                n_temp[1] = i + _ny + 1;
                n_temp[2] = i - 1;
            }
        }

        if (n_temp[0] == -1)
        {
            n_temp[0] = i - _ny - 1;
            n_temp[1] = i - 1;
            n_temp[2] = i + 1;
            n_temp[3] = i + 1 + _ny;
        }
        neighborNodes.push_back(n_temp);
        n_temp = std::vector<int>(10, -1);
    }
}

void mesh::saveMesh()
{
    std::ofstream fout;
    fout.open("mesh.dat");
    fout << " VARIABLES = \" X \", \" Y \" " << std::endl;
    fout << "ZONE T=\"dynamicMesh\", NODES=" << _points_num << ", ELEMENTS=" << _elem_num << ", DATAPACKING=POINT, ZONETYPE=FEQuadrilateral" << std::endl;
    for (int i = 0; i < _points_num; i++)
    {
        fout << mesh_points[i][0] << ' ' << mesh_points[i][1] << std::endl;
    }
    for (int i = 0; i < _elem_num; i++)
    {
        fout << elem_topo[i][0] + 1 << ' ' << elem_topo[i][1] + 1 << ' '
             << elem_topo[i][2] + 1 << ' ' << elem_topo[i][3] + 1 << std::endl;
    }
    // fout << "Neighbor Nodes" << std::endl;

    // for (int i = 0; i < _points_num; i++)
    // {
    //     fout << i << ' ';
    //     for (int j = 0; neighborNodes[i].size(); j++)
    //     {
    //         if (neighborNodes[i][j] == -1)
    //             break;
    //         fout << neighborNodes[i][j] << ' ';
    //     }
    //     fout << std::endl;
    // }

    fout.close();
}

void mesh::getTop()
{
    for (int i = 0; i < _nx + 1; i++)
    {
        top_boundary.push_back((i + 1) * _ny + i);
    }
}
void mesh::getBottom()
{
    for (int i = 0; i < _points_num; i++)
    {
        if (i % (_ny + 1) == 0)
        {
            bottom_boundary.push_back(i);
        }
    }
}

void mesh::getLeft()
{
    for (int i = 1; i < _ny; i++)
    {

        left_boundary.push_back(i);
    }
}

void mesh::getRight()
{
    for (int i = _points_num - _ny; i < _points_num - 1; i++)
    {

        right_boundary.push_back(i);
    }
}

unsigned int mesh::returnPointsNum()
{
    return _points_num;
}

std::vector<std::vector<int>> mesh::returnNeighborNodes()
{
    return neighborNodes;
}

std::vector<std::vector<double>> mesh::retrunMesh_points()
{

    return mesh_points;
}

std::vector<unsigned int> mesh::return_bottom_boundary()
{
    return bottom_boundary;
}

std::vector<unsigned int> mesh::return_top_boundary()
{
    return top_boundary;
}
std::vector<unsigned int> mesh::return_left_boundary()
{
    return left_boundary;
}

std::vector<unsigned int> mesh::return_right_boundary()
{
    return right_boundary;
}
unsigned int mesh::returnElemNum()
{
    return _elem_num;
}
std::vector<std::vector<unsigned int>> mesh::return_elem_topo()
{
    return elem_topo;
}